module windump.plugins.cred.cred;

import std.utf;

import windump.scanning.detector;
import windump.w32api;
import windump.util;
import windump.utf;
import windump.dll;

bool decodePassport(DATA_BLOB input, out DATA_BLOB output)
{
	bool ret = false;

	static ubyte[74] entropyData = [
	0xe0, 0x00, 0xc8, 0x00, 0x08, 0x01, 0x10, 0x01,
	0xc0, 0x00, 0x14, 0x01, 0xd8, 0x00, 0xdc, 0x00,
	0xb4, 0x00, 0xe4, 0x00, 0x18, 0x01, 0x14, 0x01,
	0x04, 0x01, 0xb4, 0x00, 0xd0, 0x00, 0xdc, 0x00,
	0xd0, 0x00, 0xe0, 0x00, 0xb4, 0x00, 0xe0, 0x00,
	0xd8, 0x00, 0xdc, 0x00, 0xc8, 0x00, 0xb4, 0x00,
	0x10, 0x01, 0xd4, 0x00, 0x14, 0x01, 0x18, 0x01,
	0x14, 0x01, 0xd4, 0x00, 0x08, 0x01, 0xdc, 0x00,
	0xdc, 0x00, 0xe4, 0x00, 0x08, 0x01, 0xc0, 0x00,
	0x00, 0x00 ];

	const DATA_BLOB entropy = { entropyData.length, entropyData.ptr };

	ret = CryptUnprotectData(&input, null,
			cast(DATA_BLOB*)(&entropy), null, null,
			CRYPTPROTECT_UI_FORBIDDEN, &output)
			== TRUE;

	return ret;
}

bool decodeSimple(DATA_BLOB input, out DATA_BLOB output)
{
	bool ret = false;

	ret =
		CryptUnprotectData(&input,
		null, null, null, null,
		CRYPTPROTECT_UI_FORBIDDEN, &output) == TRUE;

	return ret;
}


string decodeBlob(ubyte[] blob)
{
    assert(!(blob is null));

	DATA_BLOB data;
	DATA_BLOB pass;
	data.pbData = blob.ptr;
	data.cbData = cast(DWORD)(blob.length);
	pass.pbData = null;
	pass.cbData = 0;

	scope(exit)
	{
		if(pass.pbData !is null)
			LocalFree(cast(HGLOBAL)pass.pbData);
	}
	// Check if the blob is a Passport BLOB
	if (decodePassport(data, pass))
	{
        version(D_Version2) {
            //return toUtf8(cast(immutable(wchar)*)pass.pbData);
        }
        else return toUtf8(cast(wchar*)pass.pbData);
	}

	// Check if the blob is a BLOB from CryptProtectData with no entropy
	if (decodeSimple(data, pass))
	{
        version(D_Version2) {
            //return toUtf8(cast(immutable(wchar)*)pass.pbData);
        }
        else return toUtf8(cast(wchar*)pass.pbData);
	}

	// Check if the BLOB is an UNICODE or a MultiByte password
    wchar* wstr = cast(wchar*)blob.ptr;
    return toUTF8(wstr[0 .. blob.length / 2]);
}

import std.stdio;

class CredDetector : Detector!(CredDetector, "system/credential", "table")
{
    public this()
    {
    }


    ResultItem[] update()
    {
		
		DllImport!("advapi32.dll", "CredEnumerateW", 
				BOOL function(LPCWSTR, DWORD, DWORD*, PCREDENTIALW**)
				) CredEnumerateW;
		DllImport!("advapi32.dll", "CredFree", 
				void function(void*)) CredFree;

        DWORD count;
        PCREDENTIALW *creds;
        if(CredEnumerateW(null, 0, &count, &creds) != TRUE) {
            auto errno = GetLastError();
            if(errno == 0x490)
                throw new Exception("Cred is empty!");
            else
                throw new Exception("Failed to CredEnumerateW()");
        }

        scope(exit)
            CredFree(creds);


        //what's a wonderful world!
        //const char entropy[37] =
          //  "82BD0E67-9FEA-4748-8672-D5EFE5B779B0";
          //
        ResultItem[] ret;

        for(DWORD i = 0; i < count; ++i)
        {
            ResultItem item;

            item.provider = name();

            item.host = creds[i].TargetName == null ? "" : toUtf8(creds[i].TargetName);
            item.account = creds[i].UserName == null ? "" : toUtf8(creds[i].UserName);

            ubyte[] blob;
            blob.length = creds[i].CredentialBlobSize;
            blob[] = creds[i].CredentialBlob[0 .. creds[i].CredentialBlobSize];
            item.password = decodeBlob(blob);

            ret ~= item;

        }

        return ret;
    }

}
